/*
 This file is part of program wsprd, a detector/demodulator/decoder
 for the Weak Signal Propagation Reporter (WSPR) mode.

 File name: wsprd_utils.c

 Copyright 2001-2015, Joe Taylor, K1JT

 Most of the code is based on work by Steven Franke, K9AN, which
 in turn was based on earlier work by K1JT.

 Copyright 2014-2015, Steven Franke, K9AN

 License: GNU GPL v3

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdint.h>

#include "./nhash.h"
#include "./wsprd_utils.h"

// EVAL -- Replace strcpy & strncpy by strcpy to prevent possible buffer overflow
// #pragma GCC diagnostic ignored "-Wstringop-overflow"
// #pragma GCC diagnostic ignored "-Wstringop-truncation"

void unpack50(signed char *dat, int32_t *n1, int32_t *n2) {
    int32_t i, i4;

    i = dat[0];
    i4 = i & 255;
    *n1 = i4 << 20;

    i = dat[1];
    i4 = i & 255;
    *n1 = *n1 + (i4 << 12);

    i = dat[2];
    i4 = i & 255;
    *n1 = *n1 + (i4 << 4);

    i = dat[3];
    i4 = i & 255;
    *n1 = *n1 + ((i4 >> 4) & 15);
    *n2 = (i4 & 15) << 18;

    i = dat[4];
    i4 = i & 255;
    *n2 = *n2 + (i4 << 10);

    i = dat[5];
    i4 = i & 255;
    *n2 = *n2 + (i4 << 2);

    i = dat[6];
    i4 = i & 255;
    *n2 = *n2 + ((i4 >> 6) & 3);
}

int unpackcall(int32_t ncall, char *call) {
    char c[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
                'U', 'V', 'W', 'X', 'Y', 'Z', ' '};
    int32_t n;
    int i;
    char tmp[7];

    n = ncall;
    strcpy(call, "......");
    if (n < 262177560) {
        i = n % 27 + 10;
        tmp[5] = c[i];
        n = n / 27;
        i = n % 27 + 10;
        tmp[4] = c[i];
        n = n / 27;
        i = n % 27 + 10;
        tmp[3] = c[i];
        n = n / 27;
        i = n % 10;
        tmp[2] = c[i];
        n = n / 10;
        i = n % 36;
        tmp[1] = c[i];
        n = n / 36;
        i = n;
        tmp[0] = c[i];
        tmp[6] = '\0';
        // remove leading whitespace
        for (i = 0; i < 5; i++) {
            if (tmp[i] != c[36])
                break;
        }
        sprintf(call, "%-6s", &tmp[i]);
        // remove trailing whitespace
        for (i = 0; i < 6; i++) {
            if (call[i] == c[36]) {
                call[i] = '\0';
            }
        }
    } else {
        return 0;
    }
    return 1;
}

int unpackgrid(int32_t ngrid, char *grid) {
    char c[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
                'U', 'V', 'W', 'X', 'Y', 'Z', ' '};
    int dlat, dlong;

    ngrid = ngrid >> 7;
    if (ngrid < 32400) {
        dlat = (ngrid % 180) - 90;
        dlong = (ngrid / 180) * 2 - 180 + 2;
        if (dlong < -180)
            dlong = dlong + 360;
        if (dlong > 180)
            dlong = dlong + 360;
        int nlong = 60.0 * (180.0 - dlong) / 5.0;
        int n1 = nlong / 240;
        int n2 = (nlong - 240 * n1) / 24;
        grid[0] = c[10 + n1];
        grid[2] = c[n2];

        int nlat = 60.0 * (dlat + 90) / 2.5;
        n1 = nlat / 240;
        n2 = (nlat - 240 * n1) / 24;
        grid[1] = c[10 + n1];
        grid[3] = c[n2];
    } else {
        strcpy(grid, "XXXX");
        return 0;
    }
    return 1;
}

int unpackpfx(int32_t nprefix, char *call) {
    char nc, pfx[4] = {'\0'}, tmpcall[7];
    int i;
    int32_t n;

    strcpy(tmpcall, call);
    if (nprefix < 60000) {
        // add a prefix of 1 to 3 characters
        n = nprefix;
        for (i = 2; i >= 0; i--) {
            nc = n % 37;
            if ((nc >= 0) & (nc <= 9)) {
                pfx[i] = nc + 48;
            } else if ((nc >= 10) & (nc <= 35)) {
                pfx[i] = nc + 55;
            } else {
                pfx[i] = ' ';
            }
            n = n / 37;
        }

        char *p = strrchr(pfx, ' ');
        strcpy(call, p ? p + 1 : pfx);
        strncat(call, "/", 1);
        strncat(call, tmpcall, strlen(tmpcall));

    } else {
        // add a suffix of 1 or 2 characters
        nc = nprefix - 60000;
        if ((nc >= 0) & (nc <= 9)) {
            pfx[0] = nc + 48;
            strcpy(call, tmpcall);
            strncat(call, "/", 1);
            strncat(call, pfx, 1);
        } else if ((nc >= 10) & (nc <= 35)) {
            pfx[0] = nc + 55;
            strcpy(call, tmpcall);
            strncat(call, "/", 1);
            strncat(call, pfx, 1);
        } else if ((nc >= 36) & (nc <= 125)) {
            pfx[0] = (nc - 26) / 10 + 48;
            pfx[1] = (nc - 26) % 10 + 48;
            strcpy(call, tmpcall);
            strncat(call, "/", 1);
            strncat(call, pfx, 2);
        } else {
            return 0;
        }
    }
    return 1;
}

void deinterleave(unsigned char *sym) {
    unsigned char tmp[162];
    unsigned char p, i, j;

    p = 0;
    i = 0;
    while (p < 162) {
        j = ((i * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
        if (j < 162) {
            tmp[p] = sym[j];
            p = p + 1;
        }
        i = i + 1;
    }
    for (i = 0; i < 162; i++) {
        sym[i] = tmp[i];
    }
}

// used by qsort
int doublecomp(const void *elem1, const void *elem2) {
    if (*(const double *)elem1 < *(const double *)elem2)
        return -1;
    return *(const double *)elem1 > *(const double *)elem2;
}

int floatcomp(const void *elem1, const void *elem2) {
    if (*(const float *)elem1 < *(const float *)elem2)
        return -1;
    return *(const float *)elem1 > *(const float *)elem2;
}

int unpk_(signed char *message, char *hashtab, char *loctab, char *call_loc_pow, char *call, char *loc, char *pwr, char *callsign) {
    int n1, n2, n3, ndbm, ihash, nadd, noprint = 0;
    char grid[5], grid6[7], cdbm[4];

    unpack50(message, &n1, &n2);
    if (!unpackcall(n1, callsign))
        return 1;
    if (!unpackgrid(n2, grid))
        return 1;
    int ntype = (n2 & 127) - 64;
    callsign[12] = 0;
    grid[4] = 0;

    /*
     Based on the value of ntype, decide whether this is a Type 1, 2, or
     3 message.
     * Type 1: 6 digit call, grid, power - ntype is positive and is a member
     of the set {0,3,7,10,13,17,20...60}
     * Type 2: extended callsign, power - ntype is positive but not
     a member of the set of allowed powers
     * Type 3: hash, 6 digit grid, power - ntype is negative.
     */

    if ((ntype >= 0) && (ntype <= 62)) {
        int nu = ntype % 10;
        if (nu == 0 || nu == 3 || nu == 7) {
            ndbm = ntype;
            memset(call_loc_pow, 0, sizeof(char) * 23);
            sprintf(cdbm, "%2d", ndbm);
            strncat(call_loc_pow, callsign, strlen(callsign));
            strncat(call_loc_pow, " ", 1);
            strncat(call_loc_pow, grid, 4);
            strncat(call_loc_pow, " ", 1);
            strncat(call_loc_pow, cdbm, 2);
            strncat(call_loc_pow, "\0", 1);
            ihash = nhash(callsign, strlen(callsign), (uint32_t)146);
            strcpy(hashtab + ihash * 13, callsign);
            strcpy(loctab + ihash * 5, grid);

            memset(call, 0, sizeof(char) * strlen(callsign) + 1);
            memset(loc, 0, sizeof(char) *  strlen(grid) + 1);
            memset(pwr, 0, sizeof(char) * 2 + 1);
            strncat(call, callsign, strlen(callsign));
            strncat(call, "\0", 1);
            strncat(loc, grid, strlen(grid));
            strncat(loc, "\0", 1);
            strncat(pwr, cdbm, 2);
            strncat(pwr, "\0", 1);
        } else {
            nadd = nu;
            if (nu > 3) nadd = nu - 3;
            if (nu > 7) nadd = nu - 7;
            n3 = n2 / 128 + 32768 * (nadd - 1);
            if (!unpackpfx(n3, callsign)) return 1;
            ndbm = ntype - nadd;
            memset(call_loc_pow, 0, sizeof(char) * 23);
            sprintf(cdbm, "%2d", ndbm);
            strncat(call_loc_pow, callsign, strlen(callsign));
            strncat(call_loc_pow, " ", 1);
            strncat(call_loc_pow, cdbm, 2);
            strncat(call_loc_pow, "\0", 1);
            int nu = ndbm % 10;
            if (nu == 0 || nu == 3 || nu == 7) {  // make sure power is OK
                ihash = nhash(callsign, strlen(callsign), (uint32_t)146);
                strcpy(hashtab + ihash * 13, callsign);
            } else
                noprint = 1;
        }
    } else if (ntype < 0) {
        ndbm = -(ntype + 1);
        memset(grid6, 0, sizeof(char) * 7);
        //        size_t len=strlen(callsign);
        size_t len = 6;
        strncat(grid6, callsign + len - 1, 1);
        strncat(grid6, callsign, len - 1);
        int nu = ndbm % 10;
        if ((nu != 0 && nu != 3 && nu != 7) ||
            !isalpha(grid6[0]) || !isalpha(grid6[1]) ||
            !isdigit(grid6[2]) || !isdigit(grid6[3])) {
            // not testing 4'th and 5'th chars because of this case: <PA0SKT/2> JO33 40
            // grid is only 4 chars even though this is a hashed callsign...
            //         isalpha(grid6[4]) && isalpha(grid6[5]) ) ) {
            noprint = 1;
        }

        ihash = (n2 - ntype - 64) / 128;
        if (strncmp(hashtab + ihash * 13, "\0", 1) != 0) {
            sprintf(callsign, "<%s>", hashtab + ihash * 13);
        } else {
            sprintf(callsign, "%5s", "<...>");
        }

        memset(call_loc_pow, 0, sizeof(char) * 23);
        sprintf(cdbm, "%2d", ndbm);
        strncat(call_loc_pow, callsign, strlen(callsign));
        strncat(call_loc_pow, " ", 1);
        strncat(call_loc_pow, grid6, strlen(grid6));
        strncat(call_loc_pow, " ", 1);
        strncat(call_loc_pow, cdbm, 2);
        strncat(call_loc_pow, "\0", 1);

        memset(call, 0, sizeof(char) * strlen(callsign) + 1);
        memset(loc, 0, sizeof(char) * strlen(grid6) + 1);
        memset(pwr, 0, sizeof(char) * 2 + 1);
        strncat(call, callsign, strlen(callsign));
        strncat(call, "\0", 1);
        strncat(loc, grid6, strlen(grid6));
        strncat(loc, "\0", 1);
        strncat(pwr, cdbm, 2);
        strncat(pwr, "\0", 1);

        // I don't know what to do with these... They show up as "A000AA" grids.
        if (ntype == -64) 
            noprint = 1;
    }
    return noprint;
}
